﻿
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using UnityEditor;
using System;
using SevenZip.Compression.LZMA;
using YZL.Compress.UPK;
namespace AssetBundleHelper{
	[System.Serializable]
	public class GameVersion{
		public string[] versions;
	}


	public static class BuildAssetUnit{
		public static string abpath{
			get{
				return Application.dataPath + "/StreamingAssets/Assetbundle/";
			}
		}
		public static string GameVersionPath{
			get{
				return abpath+"GameVersion.txt";
			}
		}
		public static string CompressPath{
			get{
				return abpath + "Assetbundle";
			}
		}
		public static string sourceVerstionPath(string source){
			return abpath +source;
		}
		public static string sourceVersionABPath(string source){
			return sourceVerstionPath(source) + "/Assetbundle";
		}
		public static string BuildPath{
			get{
				return abpath + "Build/Assetbundle/";
			}
		}
		public static void ClearDireFlie(string path){
			var dire = new DirectoryInfo (path);
			var files = dire.GetFiles ();
			for (var i = 0; i < files.Length; i++) {
				File.Delete (files [i].FullName);
			}
		}
		public static void CopyFile(string path,string target){
			File.Copy (path, target);
		}
	}
		
	[System.Serializable]
	public class BuildAssetPrefabPath{
		public bool enable = true;
		public string file = "";
		public bool onlyPrefab;
		public bool IsNull{
			get{
				return file == "";
			}
		}
		public void DrawGUI(int index,int all,System.Action delecallback){
			GUILayout.BeginHorizontal ("box");
				if (index != all - 1)
					enable = EditorGUILayout.Toggle (enable, GUILayout.Width (40));
				else
					enable = true;
			EditorGUI.BeginDisabledGroup (!enable);
			if(index != all-1)
				onlyPrefab = EditorGUILayout.Toggle ("单打预制", onlyPrefab);
			GUILayout.Label (file);
			if (index == all - 1) {
				var obj = EditorGUILayout.ObjectField (null, typeof(UnityEngine.Object));
				if (obj != null) {
					file = AssetDatabase.GetAssetPath (obj);
				}
			} else {
				if (GUILayout.Button ("-")) {
					delecallback ();
				}
			}
			EditorGUI.EndDisabledGroup ();
			GUILayout.EndHorizontal ();

		}
	}
	[System.Serializable]
	public class VersionData{
		public int[] versions;
		public string date;
		public string sourceFile{
			get{
				return labVersion+"_Source";
			}
		}
		public string labVersion{
			get{
				var sb = new StringBuilder (); 
				if (versions == null)
					versions = new int[]{ 0, 0, 0, 1 };
				for (var i = 0; i < versions.Length; i++) {
					sb.Append (versions [i]);
					sb.Append (i == versions.Length-1 ? "" : ".");
				}
				return sb.ToString ();
			}
		}
		public static VersionData NextVersion(VersionData last){
			var vd = Default;
			for (var i = 0; i < vd.versions.Length; i++) {
				if (last.versions.Length > i) {
					vd.versions [i] = last.versions [i];
					if (i == vd.versions.Length - 1)
						vd.versions [i]++;
				}
			}
			return vd;
		}
		public static VersionData Default{
			get{
				VersionData data = new VersionData ();
				data.versions = new int[]{ 0, 0, 0, 1 };
				data.UpdateDate ();
				return data;
			}
		}
		public void DrawGUI(){
			GUILayout.BeginHorizontal ();
			GUILayout.Label (date);
			GUILayout.Label (labVersion);
			if (GUILayout.Button ("解压",GUILayout.Width(50))) {
				BuildAssetBundleWindow.instance.UnPackFolder (this);
			}
			GUILayout.EndHorizontal ();
		}
		public void DrawCurrentGUI(){
			GUILayout.BeginHorizontal ();
			GUILayout.Label ("版本号");
			for (var i = 0; i < versions.Length; i++) {
				versions [i] = EditorGUILayout.IntField (versions [i]);
			}
			GUILayout.EndHorizontal ();
		}
		void UpdateDate(){
			date = System.DateTime.Now.ToString ();
		}
	}
	[System.Serializable]
	public class BuildAssetBundleSettingData{
		public bool autoAddVersion;
		public bool autoCheckDepen;
		public bool autoDeleAssets;
		public BuildAssetBundleOptions aboption;
		public List<VersionData> versions = new List<VersionData>();
		public List<BuildAssetPrefabPath> prefabPath = new List<BuildAssetPrefabPath>();

		public VersionData currentVersion{
			get{
				return versions [versions.Count - 1];
			}
		}
		public VersionData lastVersion{
			get{
				return versions [versions.Count == 1 ? 0 : versions.Count - 2];
			}
		}
		private bool showAllVersion;
		public static BuildAssetBundleSettingData Load(){
			var json = MyPlayerPrefabs.getString ("BuildAssetBundleSettingData");
			if (json == null || json == "") {
				var data = new BuildAssetBundleSettingData();
				data.Check ();
				return data;
			}else{
				var data = JsonUtility.FromJson<BuildAssetBundleSettingData> (json);
				data.Check ();
				return data;
			}
		}
		void Check(){
			if (versions.Count == 0)
				versions.Add (VersionData.Default);
		}
		void Save(){
			var json = JsonUtility.ToJson (this);
			MyPlayerPrefabs.setString ("BuildAssetBundleSettingData",json);
		}
		void DrawVersion(){
			GUILayout.BeginHorizontal ("box");
			if (GUILayout.Button ("所有版本")) {
				showAllVersion = !showAllVersion;	
			}
			if (GUILayout.Button ("新增版本")) {
				if (EditorUtility.DisplayDialog ("警告", "只有在需要发布新版本的时候才需要", "新增")) {
					AddVersion ();
				}
			}
			if (versions.Count > 1 && GUILayout.Button ("回退版本")) {
				if (EditorUtility.DisplayDialog ("警告", "该操作无法还原,将移除当前版本", "回退")) {
					versions.Remove (currentVersion);
				}
			}
			GUI.backgroundColor = Color.red;
			if (GUILayout.Button ("清理版本")) {
				if (EditorUtility.DisplayDialog ("警告", "该操作无法还原,只有在初始化游戏,或者测试阶段才该使用", "清理")) {
					versions.Clear ();
					versions.Add (VersionData.Default);
				}
			}
			GUI.backgroundColor = Color.white;
			GUILayout.EndHorizontal ();
			if (showAllVersion) {
				GUILayout.BeginVertical ("box");
				for (var i = 0; i < versions.Count; i++) {
					versions [i].DrawGUI ();
				}
				GUILayout.EndVertical ();
			}
			currentVersion.DrawCurrentGUI ();
		}
		void DrawPrefabPath(){
			if (prefabPath.Count == null || prefabPath.Count == 0)
				prefabPath.Add (new BuildAssetPrefabPath ());
			for (var i = 0; i < prefabPath.Count; i++) {
				if (prefabPath [i].IsNull && i != prefabPath.Count-1) {
					prefabPath.RemoveAt (i);
					i--;
					continue;
				}

				prefabPath [i].DrawGUI (i,prefabPath.Count,delegate() {
					prefabPath.RemoveAt(i);
					i--;
				});
			}
			if (!prefabPath[prefabPath.Count-1].IsNull) {
				for (var i = 0; i < prefabPath.Count - 1; i++) {
					if (prefabPath [prefabPath.Count - 1].file == prefabPath [i].file) {
						prefabPath [prefabPath.Count - 1].file = "";
						return;
					}
				}
				prefabPath.Add (new BuildAssetPrefabPath ());
			}
		}
		public void DrawSettingGUI(){
			autoAddVersion = EditorGUILayout.Toggle ("版本自增",autoAddVersion);
			autoCheckDepen = EditorGUILayout.Toggle ("自动设置依赖",autoCheckDepen);
			autoDeleAssets = EditorGUILayout.Toggle ("自动删除压缩",autoDeleAssets);
			aboption = (BuildAssetBundleOptions)EditorGUILayout.EnumPopup ((System.Enum)(aboption));
			DrawPrefabPath ();
			DrawVersion ();

			if (GUI.changed) {
				Save ();
			}
		}
		public void AddVersion(){
			versions.Add (VersionData.NextVersion(currentVersion));
		}
	}
	public class BuildAssetBundleWindow : EditorWindow {
		static BuildAssetBundleWindow _instance;
		public static BuildAssetBundleWindow instance{
			get{
				if (_instance == null) {
					Init ();
				}
				return _instance;
			}	
		}
		BuildTarget targetPlatform;
		public BuildAssetBundleSettingData data =  BuildAssetBundleSettingData.Load ();	
		public string[] defines;
		string definesstr;
		void OnEnable(){
			defines = null;
		}
		void DrawDefine(){
			if (defines == null||defines.Length == 0) {
				definesstr = PlayerSettings.GetScriptingDefineSymbolsForGroup (BuildTargetGroup.Android);
				defines = definesstr.Split (';');
			}
			GUILayout.BeginVertical ("box");
			for(var i = 0;i < defines.Length;i++){
				DrawDefineInfo ("Resources",defines [i],i);
				DrawDefineInfo ("HTTP",defines [i],i);
			}
			GUILayout.EndVertical ();
		}
		void SetDefine(int index,bool active,string key){
			defines [index] = active ? key : ("-" + key);
			var s = "";
			for (var i = 0; i < defines.Length; i++) {
				s += defines [i];
				if (i < defines.Length - 1) {
					s += ";";
				}
			}
			PlayerSettings.SetScriptingDefineSymbolsForGroup (BuildTargetGroup.Android, s);
		}
		void DrawDefineInfo(string key,string value,int index){
			if (value.Contains (key)) {
				EditorGUILayout.BeginHorizontal ();
				GUILayout.Label (value,GUILayout.Width(100));
				var like = value == key;
				var nowlike = EditorGUILayout.Toggle (like);
				if ( nowlike!= like) {
					Debug.Log ("change");
					SetDefine (index, nowlike, key);
				}
				EditorGUILayout.EndHorizontal ();
			}
		}
		public static void RefreshLua(string path){
			var ex = Path.GetExtension(path);
			if(ex == ".lua"){
				var d = Path.GetDirectoryName (path)+"Txt";
				CreateDirectoryInfo (d);
				var newpath = d +"/lua_"+Path.GetFileNameWithoutExtension(path)+".txt";
				File.Copy(path,newpath,true);
			}
			AssetDatabase.Refresh();
		}

		[MenuItem("BuildAssetbundle/BuildWindow")]  
		static void Init(){
			_instance = GetWindow<BuildAssetBundleWindow> ();
			_instance.targetPlatform = EditorUserBuildSettings.activeBuildTarget;
		}

		void OnGUI(){
			targetPlatform = (BuildTarget)EditorGUILayout.EnumPopup ((System.Enum)targetPlatform);
			DrawDefine ();
			data.DrawSettingGUI ();
			GUILayout.BeginHorizontal ();
			if (GUILayout.Button ("依赖")) {
				CheckDependent ();
			}
			if (GUILayout.Button ("Build")) {
				Build ();
			}
			if (GUILayout.Button ("打开本地目录")) {
				Application.OpenURL (Application.persistentDataPath);
			}
			GUILayout.EndHorizontal ();
		}	

		static void CreateDirectoryInfo(string path){
			DirectoryInfo dire = new DirectoryInfo (path);
			try{
				if (!dire.Exists)
					dire.Create ();
			}catch{
				Debug.Log ("create dire error:"+path);
			}
		}
		public void UnPackFolder(VersionData data){
			var inpath = BuildAssetUnit.sourceVerstionPath (data.labVersion);
			var oupath = BuildAssetUnit.abpath;
			LZMAUnit.UnPackFolder (inpath, oupath, delegate(long fileSize, long processSize) {
				EditorUtility.DisplayProgressBar("解压文件",processSize+"/"+fileSize,(processSize/fileSize));
			});
			EditorUtility.ClearProgressBar();

			CreateDirectoryInfo (BuildAssetUnit.sourceVerstionPath(data.labVersion));
			BuildAssetUnit.ClearDireFlie (BuildAssetUnit.sourceVerstionPath (data.labVersion));
			MoveFileToOtherFile (BuildAssetUnit.CompressPath, BuildAssetUnit.sourceVerstionPath (data.labVersion));
			var dirt = new DirectoryInfo (BuildAssetUnit.CompressPath);
			dirt.Delete ();
			AssetDatabase.Refresh ();
		}
		void MoveFileToOtherFile(string source,string target){
			var disource = new DirectoryInfo (source);
			var files = disource.GetFiles ();
			for(var i =0;i < files.Length;i++){
				var ex = Path.GetExtension (files [i].FullName);
				if (ex != ".meta") {
					File.Move (files [i].FullName, target+"/"+files[i].Name);
				}else{
					File.Delete (files [i].FullName);
				}
			}
		}

		void CheckFile(){
			CreateDirectoryInfo (Application.dataPath + "/StreamingAssets");
			CreateDirectoryInfo (Application.dataPath + "/StreamingAssets/Assetbundle/");
			CreateDirectoryInfo (Application.dataPath + "/StreamingAssets/Assetbundle/Build/");
			CreateDirectoryInfo (BuildAssetUnit.BuildPath);
			CreateDirectoryInfo (BuildAssetUnit.sourceVerstionPath(data.currentVersion.sourceFile));
			CreateDirectoryInfo (BuildAssetUnit.sourceVersionABPath(data.currentVersion.sourceFile));

			CreateDirectoryInfo (BuildAssetUnit.CompressPath);
		}
		void Build(){
			if (data.autoCheckDepen)
				CheckDependent ();
			CheckFile ();
			BuildPipeline.BuildAssetBundles(BuildAssetUnit.BuildPath,data.aboption,targetPlatform);  
			MoveToSource ();
			var canbuild = MoveUnLikeAsset ();
			if (canbuild) {
				MoveBuildAsset ();
				CompressFile ();
				CreateGameVersion ();

				if (data.autoAddVersion)
					data.AddVersion ();
			} else {
				Debug.Log ("空版本");
				BuildAssetUnit.ClearDireFlie (BuildAssetUnit.sourceVersionABPath (data.currentVersion.sourceFile));
				BuildAssetUnit.ClearDireFlie (BuildAssetUnit.sourceVerstionPath (data.currentVersion.sourceFile));
				var file = new DirectoryInfo (BuildAssetUnit.sourceVersionABPath (data.currentVersion.sourceFile));
				if (file.Exists) {
					file.Delete ();
				}
				file = new DirectoryInfo (BuildAssetUnit.sourceVerstionPath (data.currentVersion.sourceFile));
				if (file.Exists) {
					file.Delete ();
				}
			}
			AssetDatabase.Refresh();

		}
		void CreateGameVersion(){
			var file = new FileInfo (BuildAssetUnit.GameVersionPath);
			if (file.Exists) {
				file.Delete ();
			}
			var gameversion = new GameVersion ();
			var list = new List<string> ();
			for (var i = 0; i < data.versions.Count; i++) {
				list.Add (data.versions [i].labVersion);
			}
			gameversion.versions = list.ToArray ();
			var json = JsonUtility.ToJson (gameversion);

			StreamWriter sw = file.CreateText ();
			sw.Write (json);
			sw.Close ();
		}

		/// <summary>
		/// 检查依赖
		/// </summary>
		void CheckDependent(){
			ClearAssetBundlesName ();
			for (var i = 0; i < data.prefabPath.Count; i++) {	
				var onlyprefab = data.prefabPath [i].onlyPrefab;
				CheckDependent (data.prefabPath [i], data.prefabPath [i].file);
			}
		}
		void CheckDependent(BuildAssetPrefabPath data,string path){
			if (path == null || path == "" || !data.enable)
				return;
			var dire = new DirectoryInfo (path);
			var dires = dire.GetDirectories ();
			for (var i = 0; i < dires.Length; i++) {
				CheckDependent (data, dires [i].FullName);
			}
			var file = dire.GetFiles ();
			for (var i = 0; i < file.Length; i++) {
				var ex = Path.GetExtension (file [i].FullName);
				if (ex != ".meta" && ex != ".lua") {
					var filepath = file [i].FullName;
					filepath = filepath.Replace ("\\", "/");
					var name = Path.GetFileNameWithoutExtension (filepath);
					var assetspath = "Assets" + filepath.Replace (Application.dataPath, "");
					AssetImporter import = AssetImporter.GetAtPath (assetspath);
					if (import != null) {
						Debug.Log ("Set" + filepath);
						import.assetBundleName = data.enable?name:"";
					}
					var de = AssetDatabase.GetDependencies (assetspath);
					for (var d = 0; d < de.Length; d++) {
						if (de [d] == assetspath)
							continue;
						var deex = Path.GetExtension(de[d]);
						if(deex == ".cs"||deex == ".js" || deex == ".lua")
							continue;
						AssetImporter dimport = AssetImporter.GetAtPath (de[d]);
						Debug.Log (data.file+"::"+(data.onlyPrefab || !data.enable));
						dimport.assetBundleName = (data.onlyPrefab || !data.enable)? "" : AssetDatabase.AssetPathToGUID (de [d]);
					}
				}
			}
		}


		void CompressFile(){
			var inpath = BuildAssetUnit.CompressPath;
			var outpath = BuildAssetUnit.sourceVerstionPath (data.currentVersion.labVersion);
			LZMAUnit.PackFolder (inpath,outpath,delegate(long fileSize, long processSize) {
				EditorUtility.DisplayProgressBar("压缩中文件",processSize+"/"+fileSize,(processSize/fileSize));
			},data.autoDeleAssets);
			EditorUtility.ClearProgressBar();
		}
		void MoveToSource(){
			var dire = new DirectoryInfo (BuildAssetUnit.BuildPath);
			var files = dire.GetFiles ();
			var target = BuildAssetUnit.sourceVersionABPath (data.currentVersion.sourceFile);
			BuildAssetUnit.ClearDireFlie (target);
			for (var i = 0; i < files.Length; i++) {
				var extension = files [i].Extension;
				if (extension != ".meta") {
					File.Move (files [i].FullName, target + "/" + files [i].Name);
				}else{
					File.Delete (files [i].FullName);
				}
			}
		}
		void MoveBuildAsset(){
			
		}
		bool MoveUnLikeAsset(){
			var currentFile = BuildAssetUnit.sourceVersionABPath (data.currentVersion.sourceFile);
			var lastFile = BuildAssetUnit.sourceVersionABPath (data.lastVersion.sourceFile);
			var targetFile = BuildAssetUnit.sourceVerstionPath (data.currentVersion.labVersion)+"/";
			targetFile = BuildAssetUnit.CompressPath+"/";
			var allcopy = false;
			var currentdire = new DirectoryInfo (currentFile);
			var lastdire = new DirectoryInfo (lastFile);
			var currentFileInfos = currentdire.GetFiles ();
			var lastFileInfos = lastdire.GetFiles ();
			BuildAssetUnit.ClearDireFlie (targetFile);
			allcopy = currentFile == lastFile;
			bool canBuild = false;
			for (var i = 0; i < currentFileInfos.Length; i++) { 
				var nowfile = currentFileInfos [i].Name;
				var ex = Path.GetExtension (currentFileInfos [i].FullName);
				if (ex == ".meta")
					continue;
				if (allcopy) {
					canBuild = true;
					BuildAssetUnit.CopyFile (currentFileInfos [i].FullName, targetFile + currentFileInfos [i].Name);
				} else {
					var sourcemd5 = MD5Tool.GetMD5HashFromFile (currentFileInfos [i].FullName);
					bool like = false;
					for (var x = 0; x < lastFileInfos.Length; x++) {
						if (nowfile == lastFileInfos [x].Name) {
							var targetmd5 = MD5Tool.GetMD5HashFromFile (lastFileInfos [x].FullName);
							if (targetmd5 != sourcemd5) {
								canBuild = true;
								BuildAssetUnit.CopyFile (currentFileInfos [i].FullName, targetFile + lastFileInfos [x].Name);
							}else{
								like = true;
								break;
							}
						} 
					}
					if (!like) {
						Debug.Log ("copy:"+currentFileInfos [i].FullName+" to:"+targetFile + currentFileInfos [i].Name+" ex" +Path.GetExtension(currentFileInfos[i].FullName));
						try{
							canBuild = true;
							BuildAssetUnit.CopyFile (currentFileInfos [i].FullName, targetFile + currentFileInfos [i].Name);
						}catch{
						}
					} else {
						continue;
					}
				}
			}
			return canBuild;
		}

		/// <summary>  
		/// 清除之前设置过的AssetBundleName，避免产生不必要的资源也打包  
		/// 之前说过，只要设置了AssetBundleName的，都会进行打包，不论在什么目录下  
		/// </summary>  
		static void ClearAssetBundlesName()  
		{  
			int length = AssetDatabase.GetAllAssetBundleNames ().Length;  
			Debug.Log (length);  
			string[] oldAssetBundleNames = new string[length];  
			for (int i = 0; i < length; i++)   
			{  
				oldAssetBundleNames[i] = AssetDatabase.GetAllAssetBundleNames()[i];  
			}  

			for (int j = 0; j < oldAssetBundleNames.Length; j++)   
			{  
				AssetDatabase.RemoveAssetBundleName(oldAssetBundleNames[j],true);  
			}  
			length = AssetDatabase.GetAllAssetBundleNames ().Length;  
			Debug.Log (length);  
		}  

	}
}